package com.fecean.aabbc.commons.easy;

import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;

/**
 ** Bean的复制对象类
 ** 基于org.springframework.cglib.beans.BeanCopier
 ** 目前不支持对象的@Builder、@Accessors(chain = true)注解，请注意使用
 * @author chenfenghai
 * @date 2019-12-11 15:46
 * @blame Android Team
 */
public class EasyBeanCopier {

    /**
     ** 使用缓存保存已创建的BeanCopier，下次使用直接从缓存中获取，提高效率
     */
    private static final ConcurrentMap<String, BeanCopier> BEAN_COPIER_MAP_CACHES = new ConcurrentHashMap<>();

    /**
     ** 转换对象
     *
     * @param <S> The source type
     * @param <T> The output type
     * @param source The source bean object
     * @param targetClass The target class
     * @return T The Copied bean object
     * @see #copy(Object, Class, BiConsumer)
     * @see BeanCopier#copy(Object, Object, Converter)
     */
    public static <S, T> T copy(S source, Class<T> targetClass) {
        return copy(source, targetClass, (BiConsumer<S, T>) null);
    }

    /**
     ** 转换对象，可自定义转换类
     *
     * @param <S> The source type
     * @param <T> The output type
     * @param source The source bean object
     * @param targetClass The target class
     * @param converter The converter copy S to T
     * @return T The Copied bean object
     * @see BeanCopier#copy(Object, Object, Converter)
     * @deprecated
     */
    @Deprecated
    public static <S, T> T copy(S source, Class<T> targetClass, Converter converter) {
        if (source == null) {
            return null;
        }
        T target = getInstance(targetClass);
        BeanCopier copier = getBeanCopier(source.getClass(), targetClass, true);
        copier.copy(source, target, converter);
        return target;
    }

    /**
     ** 转换对象，可自定义操作属性
     *
     * @param <S> The source type
     * @param <T> The output type
     * @param source The source bean object
     * @param targetClass The target class
     * @param consumer The customize bean property mapping
     * @return T The Copied bean object
     * @see BeanCopier#copy(Object, Object, Converter)
     */
    public static <S, T> T copy(S source, Class<T> targetClass, BiConsumer<S, T> consumer) {
        if (source == null) {
            return null;
        }
        T target = getInstance(targetClass);
        BeanCopier copier = getBeanCopier(source.getClass(), targetClass, false);
        copier.copy(source, target, null);
        if (consumer != null) {
            consumer.accept(source, target);
        }
        return target;
    }

    /**
     ** 复制对象属性
     *
     * @param <S> The source type
     * @param <T> The output type
     * @param source The source bean object
     * @param target The target bean object
     * @return T The Copied bean object
     * @see #copy(Object, Object, BiConsumer)
     * @see BeanCopier#copy(Object, Object, Converter)
     */
    public static <S, T> T copy(S source, T target) {
        return copy(source, target, (BiConsumer<S, T>)null);
    }

    /**
     ** 复制对象属性，可自定义操作属性
     *
     * @param <S> The source type
     * @param <T> The output type
     * @param source The source bean object
     * @param target The target bean object
     * @param converter The converter copy S to T
     * @return T The Copied bean object
     * @see BeanCopier#copy(Object, Object, Converter)
     * @see #copy(Collection, Class, BiConsumer)
     * @deprecated 不建议使用
     */
    @Deprecated
    public static <S, T> T copy(S source, T target, Converter converter) {
        if (source == null) {
            return target;
        }
        BeanCopier copier = getBeanCopier(source.getClass(), target.getClass(), true);
        copier.copy(source, target, converter);
        return target;
    }

    /**
     ** 复制对象属性，可自定义操作属性
     *
     * @param <S> The source type
     * @param <T> The output type
     * @param source The source bean object
     * @param target The target bean object
     * @param consumer The customize bean property mapping
     * @return T The Copied bean object
     * @see BeanCopier#copy(Object, Object, Converter)
     */
    public static <S, T> T copy(S source, T target, BiConsumer<S, T> consumer) {
        if (source == null) {
            return target;
        }
        BeanCopier copier = getBeanCopier(source.getClass(), target.getClass(), false);
        copier.copy(source, target, null);
        if (consumer != null) {
            consumer.accept(source, target);
        }
        return target;
    }

    /**
     ** 转换Collection中对象属性
     *
     * @param <S> The source type
     * @param <T> The output type
     * @param sourceList The sourceList collection
     * @param targetClass The target class
     * @return List<T> The result
     * @see #copy(Collection, Class, BiConsumer)
     */
    public static <S, T> List<T> copy(Collection<S> sourceList, Class<T> targetClass) {
        return copy(sourceList, targetClass, (BiConsumer<S, T>)null);
    }

    /**
     ** 转换Collection中对象属性，可自定义操作属性
     *
     * @param <S> The source type
     * @param <T> The output type
     * @param sourceList  The sourceList collection
     * @param targetClass The target class
     * @param converter The converter copy S to T
     * @return List<T> The result
     * @see #copy(Object, Object)
     * @deprecated
     */
    @Deprecated
    public static <S, T> List<T> copy(Collection<S> sourceList, Class<T> targetClass, Converter converter) {
        List<T> targetList = new ArrayList<>();
        if (CollectionUtils.isEmpty(sourceList)) {
            return targetList;
        }
        for (S sourceObject : sourceList) {
            T targetObject = copy(sourceObject, targetClass, converter);
            targetList.add(targetObject);
        }
        return targetList;
    }

    /**
     ** 转换Collection中对象属性，可自定义操作属性
     *
     * @param <S> The source type
     * @param <T> The output type
     * @param sourceList  The sourceList collection
     * @param targetClass The target class
     * @param consumer The customize bean property mapping
     * @return List<T> The result
     * @see #copy(Object, Object)
     */
    public static <S, T> List<T> copy(Collection<S> sourceList, Class<T> targetClass, BiConsumer<S, T> consumer) {
        List<T> targetList = new ArrayList<>();
        if (CollectionUtils.isEmpty(sourceList)) {
            return targetList;
        }
        for (S sourceObject : sourceList) {
            T targetObject = copy(sourceObject, targetClass);
            if (consumer != null) {
                consumer.accept(sourceObject, targetObject);
            }
            targetList.add(targetObject);
        }
        return targetList;
    }

    /**
     ** 获取BeanCopier，如果有缓存则从缓存中获取
     *
     * @param <S> The source type
     * @param <T> The output type
     * @param sourceClass The source class
     * @param targetClass The target class
     * @param useConverter The useConverter sign
     * @return BeanCopier
     * @see BeanCopier#create(Class, Class, boolean)
     */
    private static <S, T> BeanCopier getBeanCopier(Class<S> sourceClass, Class<T> targetClass, boolean useConverter) {
        String baseKey = getCacheClassKey(sourceClass, targetClass, useConverter);
        BeanCopier copier;
        if (BEAN_COPIER_MAP_CACHES.containsKey(baseKey)) {
            copier = BEAN_COPIER_MAP_CACHES.get(baseKey);
        } else {
            copier = BeanCopier.create(sourceClass, targetClass, useConverter);
            BEAN_COPIER_MAP_CACHES.put(baseKey, copier);
        }
        return copier;
    }

    /**
     ** 获取类的实例化对象
     *
     * @param <T> The output type
     * @param targetClass The targetClass object class
     * @return T The targetClass bean instance object
     * @see Class#newInstance()
     * @see Constructor#newInstance(Object...)
     */
    private static <T> T getInstance(Class<T> targetClass) {
        Objects.requireNonNull(targetClass, "The targetClass parameter is null");
        T target = null;
        try {
            target = targetClass.newInstance();
        } catch (IllegalAccessException | InstantiationException ignored) {
        }
        if(target == null){
            try{
                Constructor<T> con = targetClass.getDeclaredConstructor();
                con.setAccessible(true);
                target = con.newInstance();
            } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException ignored) {
            }
        }
        Objects.requireNonNull(target, "bean instance is null");
        return target;
    }

    /**
     ** 获取缓存的key
     *
     * @param <S> The source type
     * @param <T> The output type
     * @param sourceClass The source class
     * @param targetClass The target class
     * @param useConverter The useConverter sign
     * @return String
     */
    private static <S, T> String getCacheClassKey(Class<S> sourceClass, Class<T> targetClass, boolean useConverter) {
        return sourceClass.toString().concat("_").concat(String.valueOf(useConverter)).concat("_").concat(targetClass.toString());
    }
}
